#include "logdlg.h"
#include "logdlg_p.h"
#include "ui_logdlg.h"
#include "connection.h"

#include <QtSql>
#include <QPainter>
#include <QTextTable>
#include <QScrollBar>

using namespace DataLogger;

CustomCalendar::CustomCalendar(QWidget *parent) :
    QCalendarWidget(parent)
{

}

void CustomCalendar::insertDate(const QDate &validDate)
{
    dateSet.insert(validDate.toString(Qt::ISODate));
}

void CustomCalendar::clearDateSet()
{
    dateSet.clear();
}

void CustomCalendar::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
{
    if (dateSet.find(date.toString(Qt::ISODate)) != dateSet.constEnd())
        painter->fillRect(rect, QBrush(QColor(Qt::cyan)));

    if (date.month() != monthShown())
        painter->fillRect(rect, QBrush(QColor(Qt::gray)));

    painter->drawText(rect, Qt::AlignCenter, QString::number(date.day()));
}

LogDlgPrivate::LogDlgPrivate(LogDlg *parent) :
    QObject(parent),
    q(parent)
{

}

LogDlgPrivate::~LogDlgPrivate()
{

}

void LogDlgPrivate::initializeMember()
{
    static const QString queryFromSet = tr(
                "SELECT DISTINCT [from] FROM [LogTable] WHERE [from_port] != ''");
    static const QString queryToSet = tr(
                "SELECT DISTINCT [to] FROM [LogTable] WHERE [to_port] != ''");

    QSet<QString> fromSet, toSet;
    QSqlDatabase db = QSqlDatabase::database();
    QSqlQuery query(db);

    if (query.exec(queryFromSet))
    {
        while(query.next()) fromSet.insert(query.value(0).toString());
    }

    if (query.exec(queryToSet))
    {
        while(query.next()) toSet.insert(query.value(0).toString());
    }

    if (!fromSet.isEmpty() && !toSet.isEmpty())
    {
        foreach (const QString fromIt, fromSet)
        {
            if (toSet.find(fromIt) == toSet.constEnd())
                toSet.insert(fromIt);
        }
    }

    foreach (const QString it, toSet)
        q->ui->comboBox->addItem(it);

    calendar = new CustomCalendar(q);
    q->ui->dateEdit->setCalendarWidget(calendar);

    tableFormat.setBorder(0);
    tableFormat.setCellSpacing(3);

    updateDate();
    updateLogData();
}

void LogDlgPrivate::connectSignalsAndSlots()
{
    connect(q->ui->dateEdit,            SIGNAL(dateChanged(QDate)),
            this,                       SLOT(updateLogData()));
    connect(q->ui->comboBox,            SIGNAL(currentIndexChanged(QString)),
            this,                       SLOT(updateDate()));
    connect(q->ui->comboBox,            SIGNAL(currentIndexChanged(QString)),
            this,                       SLOT(updateLogData()));
    connect(q->ui->publicCheckBox,      SIGNAL(clicked()),
            this,                       SLOT(updateDate()));
    connect(q->ui->publicCheckBox,      SIGNAL(clicked()),
            this,                       SLOT(updateLogData()));
    connect(q->ui->publicCheckBox,      SIGNAL(clicked(bool)),
            q->ui->comboBox,            SLOT(setDisabled(bool)));
}

void LogDlgPrivate::completeConstruct()
{

}

void LogDlgPrivate::updateLogData()
{
    const QString selectedClientName = q->ui->comboBox->currentText();
    const QDate selectedDate = q->ui->dateEdit->date();

    if (q->ui->publicCheckBox->isChecked())
        updatePublicLogData(selectedDate);
    else
        updatePrivateLogData(selectedClientName, selectedDate);
}

void LogDlgPrivate::updateDate()
{
    const QString targetClientName = q->ui->comboBox->currentText();

    if (q->ui->publicCheckBox->isChecked())
        updatePublicDate();
    else
        updatePrivateDate(targetClientName);
}

/*!
  向聊天记录窗口追加数据

  \param from 消息来源
  \param time 消息时间戳
  \param message 消息内容
 */
void LogDlgPrivate::appendMessage(const QString &from, const QString &time, const QVariant &message)
{
        if (from.isEmpty() || time.isEmpty() || !message.isValid())
            return;

        const QVariant::Type mesgType = message.type();     // 消息类型
        QTextCursor mesgCursor;                             // 消息插入位置游标

        /* 插入from信息，并更新消息插入点游标到mesgCursor */
        {
            QTextCursor cursor(q->ui->textEdit->textCursor());
            cursor.movePosition(QTextCursor::End);

            QTextCharFormat format;
            format.setForeground(Qt::darkGreen);

            QTextTable *table = cursor.insertTable(2, 1, tableFormat);
            table->cellAt(0, 0).firstCursorPosition().insertText('<' + from + "> ", format);
            table->cellAt(0, 0).lastCursorPosition().insertText(time, format);

            mesgCursor = table->cellAt(1, 0).firstCursorPosition();
        }

        if (mesgType == QVariant::String)
            mesgCursor.insertText(message.toString());
        else if (mesgType == QVariant::Image)
            mesgCursor.insertImage(message.value<QImage>());

        QScrollBar *bar = q->ui->textEdit->verticalScrollBar();
        bar->setValue(bar->maximum());
}

/*!
  更新私聊记录

  \param clientName 私聊对象客户端名
  \param date 聊天日期
 */
void LogDlgPrivate::updatePrivateLogData(const QString &clientName, const QDate &date)
{
    static const QString queryPrivateLogData = tr(
                "SELECT [from], [data], [timestamp] FROM [LogTable] "
                "WHERE "
                "(([from]='%1' AND [to]='%2') "
                "OR "
                "([from]='%2' AND [to]='%1')) "
                "AND "
                "[datestamp]='%3'");

    if (clientName.isEmpty() || !date.isValid())
        return;

    updateLogDataBySql(queryPrivateLogData.arg(clientName).arg(thisAlias).arg(date.toString(Qt::ISODate)));
}

/*!
  更新群聊纪录

  \param date 聊天日期
 */
void LogDlgPrivate::updatePublicLogData(const QDate &date)
{
    static const QString queryPublicLogData = tr(
                "SELECT [from], [data], [timestamp] FROM [LogTable] "
                "WHERE [scope]=%1 "
                "AND "
                "[datestamp]='%2'");

    if (!date.isValid()) return;

    updateLogDataBySql(queryPublicLogData.arg(Connection::Public).arg(date.toString(Qt::ISODate)));
}

/*!
  根据queryLogDataSql查询结果显示聊天纪录

  \param queryLogDataSql 查询用的Sql语句
 */
void LogDlgPrivate::updateLogDataBySql(const QString &queryLogDataSql)
{
    if (queryLogDataSql.isEmpty())
        return;

    QSqlDatabase db = QSqlDatabase::database();
    QSqlQuery query(db);

    q->ui->textEdit->clear();

    if (query.exec(queryLogDataSql))
    {
        while(query.next())
        {
            const QString from = query.value(0).toString();         // 消息来源
            const QString timeStamp = query.value(2).toString();    // 消息时间戳
            QVariant data;                                          // 消息内容

            /* 反序列化data */
            {
                QByteArray rawData = query.value(1).toByteArray();
                QDataStream ds(&rawData, QIODevice::ReadOnly);

                ds >> data;
            }

            appendMessage(from, timeStamp, data);
        }
    }

    qDebug() << query.lastError().text();
}

/*!
  更新与clientName的私聊有效日期

  \param clientName 私聊对象客户端名
 */
void LogDlgPrivate::updatePrivateDate(const QString &clientName)
{
    static const QString queryPrivateValidDates = tr(
                "SELECT DISTINCT [datestamp] FROM [LogTable] "
                "WHERE "
                "([from]='%1' AND [to]='%2') "
                "OR "
                "([from]='%2' AND [to]='%1') ORDER BY [datestamp] DESC");

    if (clientName.isEmpty()) return;

    updateDateBySql(queryPrivateValidDates.arg(clientName).arg(thisAlias));
}

/*!
  更新群聊有效日期
 */
void LogDlgPrivate::updatePublicDate()
{
    static const QString queryPublicValidDates = tr(
                "SELECT DISTINCT [datestamp] FROM [LogTable] "
                "WHERE [scope]=%1 ORDER BY [datestamp] DESC").arg(Connection::Public);

    updateDateBySql(queryPublicValidDates);
}

/*!
  根据queryValidDateSql结果更新有效日期

  \param queryValidDateSql 查询用的Sql语句
 */
void LogDlgPrivate::updateDateBySql(const QString &queryValidDateSql)
{
    if (queryValidDateSql.isEmpty())
        return;

    QSqlDatabase db = QSqlDatabase::database();
    QSqlQuery query(db);

    calendar->clearDateSet();

    if (query.exec(queryValidDateSql))
    {
        /* 更新私聊（对象为clientName）有效日期到日历 */
        while(query.next())
        {
            const QString dateStampStr = query.value(0).toString();
            const QDate dateStamp = QDate::fromString(dateStampStr, Qt::ISODate);

            calendar->insertDate(dateStamp);
        }

        /* 获取最近有效日期，并设置到dateEdit */
        if (query.isActive() && query.isSelect() && query.first())
        {
            q->ui->dateEdit->setDate(QDate::fromString(query.value(0).toString(), Qt::ISODate));
        }
    }
}

LogDlg::LogDlg(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::LogDlg),
    d(new LogDlgPrivate(this))
{
    ui->setupUi(this);
    d->initializeMember();
    d->connectSignalsAndSlots();
    d->completeConstruct();
}

LogDlg::~LogDlg()
{
    delete ui;
    delete d;
}
